﻿//------------------------------------------------------------
// AureFramework
// Developed By Drunk Fish
// GitHub: https://github.com/YYYurz
// Gitee: https://gitee.com/yyyurz
//------------------------------------------------------------

using System;
using System.Collections.Generic;
using AureFramework.ObjectPool;
using AureFramework.ReferencePool;
using AureFramework.Resource;
using UnityEngine;

namespace AureFramework.Entity
{
	public sealed partial class EntityModule : AureFrameworkModule, IEntityModule
	{
		/// <summary>
		/// 内部实体组
		/// </summary>
		[Serializable]
		private sealed class EntityGroup : IEntityGroup
		{
			private readonly LinkedList<EntityObject> entityObjectLinked = new();
			private readonly List<int> loadingEntityIdList = new();
			private LinkedListNode<EntityObject> cacheNode;
			private IObjectPool<EntityObject> entityObjectPool;
			private IResourceModule resourceModule;
			private IReferencePoolModule referencePoolModule;
			private InstantiateGameObjectCallbacks loadAssetCallbacks;
			private GameObject root;

			[SerializeField] private string groupName;
			[SerializeField] private int entityPoolCapacity;
			[SerializeField] private float entityPoolExpireTime;
			
			/// <summary>
			/// 初始化
			/// </summary>
			public void Init(GameObject obj)
			{
				root = obj;
				resourceModule = Aure.GetModule<IResourceModule>();
				referencePoolModule = Aure.GetModule<IReferencePoolModule>();
				entityObjectPool = Aure.GetModule<IObjectPoolModule>().CreateObjectPool<EntityObject>("Entity Group " + groupName, entityPoolCapacity, entityPoolExpireTime);
				loadAssetCallbacks = new InstantiateGameObjectCallbacks(null, OnLoadAssetSuccess, null, OnLoadAssetFailed);
			}

			/// <summary>
			/// 轮询
			/// </summary>
			/// <param name="elapseTime"></param>
			/// <param name="realElapseTime"></param>
			public void Update(float elapseTime, float realElapseTime)
			{
				ProcessUpdate(elapseTime, realElapseTime);
			}

			/// <summary>
			/// 物理轮询
			/// </summary>
			/// <param name="fixedElapseTime"></param>
			/// <param name="realFixedElapseTime"></param>
			public void FixedUpdate(float fixedElapseTime, float realFixedElapseTime)
			{
				ProcessFixedUpdate(fixedElapseTime, realFixedElapseTime);
			}

			/// <summary>
			/// 获取实体组名称
			/// </summary>
			public string Name
			{
				get
				{
					return groupName;
				}
			}

			/// <summary>
			/// 获取实体组中实体数量
			/// </summary>
			public int EntityCount
			{
				get
				{
					return entityObjectLinked.Count;
				}
			}

			/// <summary>
			/// 获取或设置实体对象池容量
			/// </summary>
			public int EntityPoolCapacity
			{
				get
				{
					return entityPoolCapacity;
				}
				set
				{
					entityObjectPool.Capacity = entityPoolCapacity = value;
				}
			}

			/// <summary>
			/// 获取或设置实体对象池过期时间
			/// </summary>
			public float EntityPoolExpireTime
			{
				get
				{
					return entityPoolExpireTime;
				}
				set
				{
					entityObjectPool.ExpireTime = entityPoolExpireTime = value;
				}
			}

			/// <summary>
			/// 是否已经存在EntityLogic
			/// </summary>
			/// <param name="entityLogic"></param>
			/// <returns></returns>
			public bool ContainsEntity(EntityLogic entityLogic)
			{
				foreach (var entityObject in entityObjectLinked)
				{
					if (entityObject.EntityLogic!= null && entityObject.EntityLogic.Equals(entityLogic))
					{
						return true;
					}
				}

				return false;
			}

			/// <summary>
			/// 获取实体
			/// </summary>
			/// <param name="entityId"></param>
			/// <returns></returns>
			public EntityLogic GetEntity(int entityId)
			{
				foreach (var entityObject in entityObjectLinked)
				{
					if (entityObject.Id == entityId)
					{
						return entityObject.EntityLogic;
					}
				}
				
				return null;
			}

			/// <summary>
			/// 预加载实体
			/// </summary>
			/// <param name="entityLogicType"></param>
			/// <param name="entityId"></param>
			/// <param name="entityAssetName"></param>
			/// <param name="callback"></param>
			public void PreloadEntity(Type entityLogicType, int entityId, string entityAssetName, Action<bool> callback)
			{
				var entityInfo = EntityInfo.Create(entityId, true, entityLogicType, null, callback);
				resourceModule.InstantiateAsync(entityAssetName, loadAssetCallbacks, entityInfo);
				loadingEntityIdList.Add(entityId);
			}

			/// <summary>
			/// 显示实体
			/// </summary>
			/// <param name="entityLogicType">实体逻辑类</param>
			/// <param name="entityId">实体Id</param>
			/// <param name="entityAssetName">实体资源名称</param>
			/// <param name="userData">用户自定义数据</param>
			public void ShowEntity(Type entityLogicType, int entityId, string entityAssetName, object userData)
			{
				if (InternalTryGetAvailableEntityObject(entityAssetName, out var entityObject))
				{
					entityObject.OnShow(entityId, userData);
					entityObjectLinked.AddLast(entityObject);
					return;
				}

				var entityInfo = EntityInfo.Create(entityId, false, entityLogicType, userData, null);
				resourceModule.InstantiateAsync(entityAssetName, loadAssetCallbacks, entityInfo);
				loadingEntityIdList.Add(entityId);
			}
			
			/// <summary>
			/// 隐藏实体
			/// </summary>
			/// <param name="entityLogic">实体</param>
			/// <returns></returns>
			public bool HideEntity(EntityLogic entityLogic)
			{
				return HideEntity(entityLogic.Id);
			}

			/// <summary>
			/// 隐藏实体
			/// </summary>
			/// <param name="entityId">实体Id</param>
			/// <returns></returns>
			public bool HideEntity(int entityId)
			{
				if (IsEntityLoading(entityId))
				{
					loadingEntityIdList.Remove(entityId);
					return true;
				}
				
				var entityObject = GetEntityObject(entityId);
				if (entityObject == null)
				{
					return false;
				}
				
				if (cacheNode != null && cacheNode.Value == entityObject)
				{
					cacheNode = cacheNode.Next;
				}
				
				entityObject.OnHide();
				entityObjectPool.Recycle(entityObject);
				entityObjectLinked.Remove(entityObject);

				return true;
			}

			/// <summary>
			/// 隐藏所有实体
			/// </summary>
			public void HideAllEntities()
			{
				foreach (var entityObject in entityObjectLinked)
				{
					entityObject.OnHide();
					entityObjectPool.Recycle(entityObject);
				}
				
				loadingEntityIdList.Clear();
				entityObjectLinked.Clear();
			}

			private bool InternalTryGetAvailableEntityObject(string entityAssetName, out EntityObject entityObject)
			{
				if (entityObjectPool.TrySpawn(entityAssetName, out entityObject))
				{
					return true;
				}

				return false;
			}
			
			private bool InternalTryCreateEntityObject(out EntityObject entityObject)
			{
				entityObject = default;
				if (entityObjectPool.Full)
				{
					Debug.LogError($"Entity object pool has reached its maximum capacity, group name :{groupName}");
					return false;
				}

				entityObject = referencePoolModule.Acquire<EntityObject>();

				return true;
			}

			private void ProcessUpdate(float elapseTime, float realElapseTime)
			{
				var current = entityObjectLinked.First;
				while (current != null)
				{
					cacheNode = current.Next;
					current.Value.OnUpdate(elapseTime, realElapseTime);
					current = cacheNode;
					cacheNode = null;
				}
			}
			
			private void ProcessFixedUpdate(float fixedElapseTime, float realFixedElapseTime)
			{
				var current = entityObjectLinked.First;
				while (current != null)
				{
					cacheNode = current.Next;
					current.Value.OnFixedUpdate(fixedElapseTime, realFixedElapseTime);
					current = cacheNode;
					cacheNode = null;
				}
			}

			private bool IsEntityLoading(int entityId)
			{
				return loadingEntityIdList.Contains(entityId);
			}

			private EntityObject GetEntityObject(int entityId)
			{
				foreach (var entityObject in entityObjectLinked)
				{
					if (entityObject.Id != entityId)
					{
						continue;
					}

					return entityObject;
				}

				return null;
			}

			/// <summary>
			/// 加载成功，进对象池
			/// </summary>
			/// <param name="assetName"></param>
			/// <param name="taskId"></param>
			/// <param name="asset"></param>
			/// <param name="userData"></param>
			private void OnLoadAssetSuccess(string assetName, int taskId, GameObject asset, object userData)
			{
				var entityInfo = (EntityInfo)userData;
				if (!InternalTryCreateEntityObject(out var entityObject))
				{
					Debug.LogError($"Entity object pool is full, group name:{groupName}");
					resourceModule.ReleaseInstance(asset);
				}
				else if(!IsEntityLoading(entityInfo.EntityId))
				{
					resourceModule.ReleaseInstance(asset);
				}
				else
				{
					var entityLogic = (EntityLogic)asset.AddComponent(entityInfo.EntityLogicType);
					asset.transform.SetParent(root.transform);
					asset.gameObject.SetActive(false);
					entityObject.OnInit(this, asset, entityLogic);
					
					if(entityInfo.IsPreload)
					{
						entityObjectPool.Register(entityObject, false, assetName);
					}
					else
					{
						entityObjectPool.Register(entityObject, true, assetName);
						entityObject.OnShow(entityInfo.EntityId, entityInfo.UserData);
						entityObjectLinked.AddLast(entityObject);
					}
				}

				loadingEntityIdList.Remove(entityInfo.EntityId);
				entityInfo.Callback?.Invoke(true);
				referencePoolModule.Release(entityInfo);
			}

			/// <summary>
			/// 加载失败直接释放掉，不需要进对象池
			/// </summary>
			/// <param name="assetName"></param>
			/// <param name="taskId"></param>
			/// <param name="errorMessage"></param>
			/// <param name="userData"></param>
			private void OnLoadAssetFailed(string assetName, int taskId, string errorMessage, object userData)
			{
				var entityInfo = (EntityInfo)userData;
				entityInfo.Callback?.Invoke(false);
				referencePoolModule.Release(entityInfo);
				Debug.LogError($"Load entity asset failed. Asset name :{assetName}, error message :{errorMessage}");
			}
		}	
	}
}